package com.zxy.supplier_system.server.service

import com.zxy.supplier_system.server.controller.MarketReturnOrderController
import com.zxy.supplier_system.server.controller.MarketReturnOrdersItemController
import com.zxy.supplier_system.server.controller.MarketsReturnOrderController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.repository.*
import com.zxy.supplier_system.server.utils.*
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.repository.findByIdOrNull
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.server.ResponseStatusException
import java.time.OffsetDateTime
import java.util.*

@Service
class MarketReturnOrderService(
    private val marketReturnOrderRepository: MarketReturnOrderRepository,
    private val marketRepository: MarketRepository,
    private val marketReturnOrderItemRepository: MarketReturnOrderItemRepository,
    private val employeeRepository: EmployeeRepository,
    private val systemServiceService: SystemServiceService,
    private val goodsRepository: GoodsRepository,
    private val notificationService: NotificationService,
    private val employeeService: EmployeeService,
    private val employeeOperateRecordService: EmployeeOperateRecordService,
) {

    companion object{
        val log by LoggerDelegate()
    }

    @Transactional(readOnly = true)
    fun queryMarketReturnOrder(
        queryMarketReturnOrderRequest: MarketReturnOrderController.QueryMarketReturnOrderRequest,
        pageable: Pageable
    ): Page<MarketReturnOrderController.QueryMarketReturnOrderResponse> {

        val employee = employeeService.getRequiredCurrentEmployee()
        return this.marketReturnOrderRepository.findAll(
            Specification.allOf(
                MarketReturnOrderRepository.Specifications.marketIn(queryMarketReturnOrderRequest.marketIds),
                MarketReturnOrderRepository.Specifications.endDateLessThanOrEqualTo(queryMarketReturnOrderRequest.endDate),
                MarketReturnOrderRepository.Specifications.startDateGreaterThanOrEqualTo(queryMarketReturnOrderRequest.startDate),
                MarketReturnOrderRepository.Specifications.employeeIn(queryMarketReturnOrderRequest.createdByIds),
                MarketReturnOrderRepository.Specifications.statusEqual(queryMarketReturnOrderRequest.status),
            ).and(
                if (employee.authorities.contains(Authority.卖场_订单_查询所有)) {
                    Specification.where(null)
                } else {
                    MarketReturnOrderRepository.Specifications.marketSaleManagerEqual(employee.id!!)
                        .or(MarketReturnOrderRepository.Specifications.marketShoppingGuidesContains(employee.id!!))
                }
            ), pageable
        ).map { marketReturnOrder ->
            MarketReturnOrderController.QueryMarketReturnOrderResponse(
                marketReturnOrder.id,
                marketReturnOrder.createdBy.let {
                    IntIdAndName(it.id!!, it.name)
                },
                marketReturnOrder.createdDateTime,
                marketReturnOrder.lastModifiedDateTime,
                marketReturnOrder.goodsList.size,
                marketReturnOrder.market.let {
                    IntIdAndName(it.id, it.name)
                },
                marketReturnOrder.status,
            )
        }
    }

    @Transactional
    fun createMarketReturnOrder(marketId: Int, request: MarketsReturnOrderController.CreateMarkerReturnOrderRequest): Int {
        val marketReturnOrder = this.marketReturnOrderRepository.save(MarketReturnOrder().apply {
            this.market = marketRepository.findByIdOrNull(marketId).orElse404()
            this.note = request.note
        })
        this.employeeOperateRecordService.createMarketReturnOrder(marketReturnOrder)
        return marketReturnOrder.id
    }

    @Transactional
    fun updateOrderGoods(
        id: Int,
        updateOrderGoodsRequest: MarketReturnOrderController.UpdateOrderGoodsRequest
    ) {
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdOrNull(id).orElse404()
        marketReturnOrder.goodsList.clear()
        marketReturnOrder.goodsList.addAll(
            updateOrderGoodsRequest.goodsList.mapIndexed { index, it ->
                MarketReturnOrderItem().apply {
                    this.order = marketReturnOrder
                    this.amount = it.amount
                    this.goods =
                        goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(it.id, systemServiceService.getCurrentSystemService())
                            .orElse404()
                    this.sequence = index
                }
            }
        )
        this.marketReturnOrderRepository.save(marketReturnOrder)
    }

    @Transactional(readOnly = true)
    fun getMarketReturnOrderDetail(id: Int, includeItems: Boolean): MarketReturnOrderController.MarketReturnOrderDetail {
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdOrNull(id).orElse404()
        return MarketReturnOrderController.MarketReturnOrderDetail(
            marketReturnOrder.id,
            marketReturnOrder.createdDateTime,
            IntIdAndName(marketReturnOrder.createdBy.id!!, marketReturnOrder.createdBy.name),
            IntIdAndName(marketReturnOrder.market.id, marketReturnOrder.market.name),
            if (!includeItems) emptyList() else marketReturnOrder.goodsList.map { item ->
                val goods = item.goods
                goods.let { it ->
                    MarketReturnOrderController.MarketReturnOrderItemResponse(
                        it.id,
                        it.name, it.barcode,
                        it.price,
                        it.amount,
                        it.createdDateTime,
                        it.lastModifiedDateTime,
                        it.brand?.toIntIdAndName(),
                        it.category?.toIntIdAndName(),
                        it.factory?.toIntIdAndName(),
                        item.amount,
                        it.boxSpecification,
                    )
                }
            },
            marketReturnOrder.submittedDateTime,
            marketReturnOrder.validatedDateTime,
        )
    }

    @Transactional
    fun addItem(orderId: Int, addMarketReturnOrdersItemRequest: MarketReturnOrdersItemController.AddMarketReturnOrdersItemRequest) {
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdOrNull(orderId).orElse404()
        marketReturnOrder.goodsList.add(MarketReturnOrderItem().apply {
            this.order= marketReturnOrder
            this.goods = goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(
                addMarketReturnOrdersItemRequest.goodsId,
                systemServiceService.getCurrentSystemService()
            ).orElse404()
            this.amount = addMarketReturnOrdersItemRequest.amount
            this.sequence = marketReturnOrder.goodsList.nextSequence
        })
        this.marketReturnOrderRepository.save(marketReturnOrder)
        this.employeeOperateRecordService.addMarketReturnOrderItem(marketReturnOrder)
    }

    @Transactional
    fun deleteItem(orderId: Int, goodsId: Int) {

        val marketReturnOrderItem = this.marketReturnOrderItemRepository.findByIdOrNull(MarketReturnOrderItem.UnionId().apply {
            this.order = orderId
            this.goods = goodsId
        })
        if (marketReturnOrderItem != null) {
            this.marketReturnOrderItemRepository.delete(marketReturnOrderItem)
            this.employeeOperateRecordService.deleteMarketReturnOrderItem(marketReturnOrderItem)
        }
    }

    @Transactional
    fun updateItemAmount(
        goodsId: Int,
        orderId: Int,
        updateItemAmountRequest: MarketReturnOrdersItemController.UpdateItemAmountRequest
    ) {
        val marketReturnOrderItem = this.marketReturnOrderItemRepository.findByIdOrNull(MarketReturnOrderItem.UnionId().apply {
            this.order= orderId
            this.goods = goodsId
        }).orElse404()
        marketReturnOrderItem.amount = updateItemAmountRequest.amount
        this.marketReturnOrderItemRepository.save(marketReturnOrderItem)
        this.employeeOperateRecordService.updateMarketReturnOrderItemAmount(marketReturnOrderItem)
    }

    fun getAllEmployees(): List<IntIdAndName> {
        return this.employeeRepository.findAll(
            EmployeeRepository.Specifications.haveMarketReturnOrder().and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            )
        ).map {
            IntIdAndName(it.id!!, it.name)
        }
    }

    @Transactional
    fun queryMarketReturnOrderInMarket(
        marketId: Int,
        pageable: Pageable
    ): Slice<MarketsReturnOrderController.QueryMarketReturnOrderInMarketResponse> {
        return this.marketReturnOrderRepository.findAllByMarket_Id(marketId, pageable).map {
            MarketsReturnOrderController.QueryMarketReturnOrderInMarketResponse(
                it.id,
                it.createdBy.toIntIdAndName(),
                it.createdDateTime,
                it.goodsList.size,
                it.submittedDateTime,
                it.status,
            )
        }
    }

    fun queryOrderItemsInOrder(
        orderId: Int,
        pageable: Pageable
    ): Page<MarketReturnOrdersItemController.OrderItemInOrderSummary> {
        return this.marketReturnOrderItemRepository.findAllByOrder_IdOrderBySequenceAsc(orderId, pageable).map {
            val goods = it.goods
            MarketReturnOrdersItemController.OrderItemInOrderSummary.fromGoodsAndAmount(goods, it.amount)
        }
    }

    fun exists(orderId: Int, goodsId: Int): Boolean {
        return this.marketReturnOrderItemRepository.existsByOrder_IdAndGoods_Id(orderId, goodsId)
    }

    fun exists(orderId: Int, barcode: String): Boolean {
        return this.marketReturnOrderItemRepository.existsByOrder_IdAndGoods_Barcode(orderId, barcode)
    }


    @Transactional
    fun addItem(orderId: Int, barcode: String): MarketReturnOrdersItemController.OrderItemInOrderSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdAndMarket_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByBarcodeAndSystemServiceAndDisableIsFalse(barcode, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(marketReturnOrder, goods)
    }

    @Transactional
    fun submitMarketReturnOrder(id: Int) {
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdAndMarket_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        if (marketReturnOrder.submittedDateTime == null) {
            marketReturnOrder.submittedDateTime = OffsetDateTime.now()
            this.marketReturnOrderRepository.save(marketReturnOrder)
            this.employeeOperateRecordService.submitMarketReturnOrder(marketReturnOrder)
            marketReturnOrder.market.saleManager?.let {
                this.notificationService.pushNotification(Notification.Type.SUBMITTED_MARKET_ORDER, it, OnlyIntId(id))
            }
        } else {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST)
        }
    }

    @Transactional
    fun addItem(orderId: Int, goodsId: Int): MarketReturnOrdersItemController.OrderItemInOrderSummary {
        val currentSystemService = systemServiceService.getCurrentSystemService()
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdAndMarket_SystemService(
            orderId,
            currentSystemService
        ).orElse404()
        val goods =
            goodsRepository.findByIdAndSystemServiceAndDisableIsFalse(goodsId, currentSystemService)
                .orElse404()
        return orderItemInOrderSummary(marketReturnOrder, goods)
    }

    private fun orderItemInOrderSummary(
        marketReturnOrder: MarketReturnOrder,
        goods: Goods
    ): MarketReturnOrdersItemController.OrderItemInOrderSummary {
        val amount = 1
        marketReturnOrder.goodsList.add(MarketReturnOrderItem().apply {
            this.order = marketReturnOrder
            this.goods =
                goods
            this.amount = amount
            this.sequence = marketReturnOrder.goodsList.nextSequence
        })
        this.marketReturnOrderRepository.save(marketReturnOrder)
        this.employeeOperateRecordService.addMarketReturnOrderItem(marketReturnOrder)
        return MarketReturnOrdersItemController.OrderItemInOrderSummary.fromGoodsAndAmount(goods, amount)
    }

    @Transactional
    fun validate(id: Int): MarketReturnOrder {
        val marketReturnOrder = this.marketReturnOrderRepository.findByIdAndMarket_SystemService(
            id,
            systemServiceService.getCurrentSystemService()
        ).orElse404()
        if (marketReturnOrder.submittedDateTime == null) {
            throw ResponseStatusException(HttpStatus.BAD_REQUEST, "订单未提交")
        }
        marketReturnOrder.goodsList.forEach {item->
            val marketGoods = marketReturnOrder.market.goodsList.find {
                it.goods.id == item.goods.id
            }
            if (marketGoods!=null){
                marketGoods.amount -= item.amount
            }else{
                log.error("marketGoods not found ${item.goods.id} ${item.goods.name}")
            }
        }
        this.marketRepository.save(marketReturnOrder.market)
        for (item in marketReturnOrder.goodsList) {
            val goods = item.goods
            goods.amount += item.amount
            goodsRepository.save(goods)
        }
        marketReturnOrder.validatedDateTime = OffsetDateTime.now()
        this.employeeOperateRecordService.validatedMarketReturnOrder(marketReturnOrder)
        return this.marketReturnOrderRepository.save(marketReturnOrder)
    }

    @Transactional
    fun delete(id: Int) {
        val employee = this.employeeService.getRequiredCurrentEmployee()
        val marketReturnOrder = this.marketReturnOrderRepository.findBy<MarketReturnOrder, Optional<MarketReturnOrder>>(
            Specification.allOf(MarketReturnOrderRepository.Specifications.ifUCanSee(employee)).and(
                MarketReturnOrderRepository.Specifications.systemServiceEqual(
                    systemServiceService.getCurrentSystemService(),
                ),
            ).and(MarketReturnOrderRepository.Specifications.idEqual(id)).and(
                MarketReturnOrderRepository.Specifications.submittedDateTimeIsNull()
            )
        ) {
            it.one()
        }.orElse(null).orElse404()
        this.marketReturnOrderRepository.delete(marketReturnOrder)
        this.employeeOperateRecordService.deleteMarketReturnOrder(marketReturnOrder)
    }

    fun queryOrderCreatedEmployeesByMarketId(marketId: Int): List<IntIdAndName> {
        return this.employeeRepository.findAll(
            EmployeeRepository.Specifications.haveMarketsReturnOrder(marketId).and(
                SystemServiceRepository.Specifications.systemServiceEquals(
                    { root -> root },
                    systemServiceService.getCurrentSystemService()
                )
            )
        ).map {
            IntIdAndName(it.id!!, it.name)
        }
    }
}